﻿using System.Reflection;
using Castle.DynamicProxy;
using Common.Cache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Common.Autofac.Aop;

/// <summary>
/// 全局拦截器
/// </summary>
public class GlobalInterceptor : IInterceptor
{
    private readonly ILogger<GlobalInterceptor> _logger;
    private readonly IList<ICacheManager> _cacheManager;
    private readonly DbContext _appDbContext;

    private int _transactionCount = 0;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="cacheManager">cache manager</param>
    /// <param name="appDbContext">Ef db context</param>
    public GlobalInterceptor(ILogger<GlobalInterceptor> logger, IList<ICacheManager> cacheManager,
        DbContext appDbContext)
    {
        _logger = logger;
        _cacheManager = cacheManager;
        _appDbContext = appDbContext;
        _logger.LogInformation("GlobalInterceptor Init");
    }

    private ICacheManager? detectCacheManager(string managerName)
    {
        if (_cacheManager == null || !_cacheManager.Any())
        {
            throw new Exception("没有CacheManager可用");
        }
        
        if (string.IsNullOrWhiteSpace(managerName))
        {
            return _cacheManager.FirstOrDefault();
        }

        return _cacheManager
            .FirstOrDefault(it => it.GetCacheName().Equals(managerName, StringComparison.OrdinalIgnoreCase));
    }

    /// <summary>
    /// 实现方法拦截
    /// </summary>
    /// <param name="invocation"></param>
    public void Intercept(IInvocation invocation)
    {
        _logger.LogDebug("global aop begin, method: {0}", invocation.Method.Name);

        var realType = invocation.InvocationTarget.GetType();
        var methodInfo = realType.GetMethod(invocation.Method.Name, BindingFlags.Public | BindingFlags.Instance);

        var cacheableAttribute = methodInfo?.GetCustomAttribute<CacheableAttribute>();

        // var cacheableAttribute = findCacheableAttribute(invocation);
        if (cacheableAttribute != null)
        {
            var key = cacheableAttribute.Key;
            if (string.IsNullOrWhiteSpace(key))
            {
                var argStringValueList = invocation.Arguments.Select(it => it.ToString());
                // invocation.Method.GetParameters();
                key = $"{string.Join("_", argStringValueList)}";
            }

            var cacheName = $"DEFAULT:{key}";
            if (!string.IsNullOrWhiteSpace(cacheableAttribute.CacheName))
            {
                cacheName = $"{cacheableAttribute.CacheName}:{key}";
            }

            var cacheManager = detectCacheManager(cacheableAttribute.CacheManager);

            var returnValue = cacheManager.Get(cacheName);
            if (returnValue != null)
            {
                invocation.ReturnValue = returnValue;
                _logger.LogInformation("从缓存读取[cache name: {0}]", cacheName);
            }
            else
            {
                invocation.Proceed();
                returnValue = invocation.ReturnValue;
                cacheManager.Set(cacheName, returnValue, cacheableAttribute.AbsExpireSeconds);
            }

            return;
        }

        var transAttr = methodInfo?.GetCustomAttribute(typeof(TransactionAttribute));
        if (transAttr != null)
        {
            _transactionCount++;
            var currentTransaction = _appDbContext.Database.CurrentTransaction;
            if (currentTransaction == null)
            {
                _appDbContext.Database.BeginTransaction();
            }

            try
            {
                invocation.Proceed();
                _transactionCount--;
                if (_transactionCount == 0)
                {
                    _appDbContext.Database.CommitTransaction();
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "事务异常");
                _appDbContext.Database.RollbackTransaction();
                throw;
            }
        }
        else
        {
            invocation.Proceed();
        }


        _logger.LogDebug("global aop end");
    }


}